//------------------------------------------------------------
//  Filename: eth_mac_rxdma.sv
//   
//  Author  : wlduan@gmail.com
//  Revise  : 2020-12-01 19:05
//  Description: 
//   
//  All Rights Reserved.                                       
//-------------------------------------------------------------
//
`timescale 1ns/1ps
 
module eth_mac_rxdma #(
    parameter BD_WCNT = 2
)( 
    input  logic       clk_i,
    input  logic       rstn_i,
 
    input  logic       dma_rx_en,
    input  logic[9:0]  dma_rx_data,
    input  logic       dma_rx_valid,
    output logic       dma_rx_ready,
    output logic       dma_rx_busy,
    output logic[3:0]  dma_rx_err,
    output logic[6:0]  dma_rx_rptr,

    input  logic       mac_ft_err,

    BDU_IF.Master      bdu_master,
    BDU_IF.Master      mem_master 
);
//--------------------------------------------------------
logic            rx_sof;
logic            rx_eof;
logic            rx_err;
logic[7:0]       rx_data;
logic[15:0]      rx_bcnt_q;
logic            buf_eof;
logic[15:0]      bdu_sz;
logic            bdu_sof;
logic            bdu_eof;
logic            bdu_err;
logic            bdu_req;
logic[3:0]       bdu_rd_cnt;
logic[3:0]       bdu_rd_cnt_q;
logic            dma_reset;
logic            dma_bdu_op;    
logic[31:0]      dma_bdu_addr;
logic[31:0]      dma_mem_addr;
logic[31:0]      dma_mem_data;
logic            dma_mem_valid;
//--------------------------------------------------- 
//----- 
//-----  B2W FIFO
//-----
//--------------------------------------------------- 
logic[31:0]      buf_bits_q;
logic[31:0]      buf_bits;
logic[1:0]       b_ptr;
logic            bits_wr;
logic            bits_rd;
//--------------------------------------------------- 
assign           bits_wr = dma_rx_ready&dma_rx_valid;
assign           bits_rd = dma_rx_ready&((bits_wr&(b_ptr == 3))||((b_ptr > 0)&rx_eof));
//--------------------------------------------------- 
always_ff@(posedge clk_i,negedge rstn_i)begin
    if(!rstn_i)begin 
        b_ptr <= '0;    
        buf_bits_q <= '0;    
    end     
    else if(bits_wr) begin
        b_ptr <= b_ptr + 2'b1;
        buf_bits_q <= buf_bits;
    end
end
//--------------------------------------------------- 
always_comb begin
    buf_bits = buf_bits_q;
    if(bits_wr) buf_bits[b_ptr*8 +: 8] = rx_data;;
end
//--------------------------------------------------- 
always_ff@(posedge clk_i,negedge rstn_i)begin
    if(!rstn_i)begin 
        dma_mem_data  <= 'b0;
        dma_mem_valid <= 'b0;
    end     
    else begin
        dma_mem_data  <= buf_bits;
        dma_mem_valid <= bits_rd;
    end
end
//--------------------------------------------------- 
//----- 
//-----  RX DMA
//-----
//--------------------------------------------------- 
assign {rx_sof,rx_eof,rx_data} = dma_rx_data;
assign buf_eof = rx_eof&dma_rx_valid&dma_rx_ready;
assign rx_err  = mac_ft_err;
//--------------------------------------------------------
enum logic[3:0] {DMA_IDLE,DMA_NOP,DMA_CSR,DMA_DATA,DMA_LAST} dma_cs,dma_ns;
//--------------------------------------------------------
always_ff @(posedge clk_i,negedge rstn_i) begin 
    if(rstn_i == 0)begin 
        dma_cs <= DMA_IDLE;
    end
    else begin 
        dma_cs <= dma_ns;
    end
end
//--------------------------------------------------------
always_comb begin 
    dma_ns = dma_cs;
    case(dma_cs) 
        DMA_IDLE: begin 
            if(dma_reset) dma_ns = DMA_NOP ;
        end
        DMA_NOP: begin
            dma_ns = DMA_CSR ;
        end
        DMA_CSR: begin 
            if(bdu_rd_cnt == BD_WCNT) dma_ns = DMA_DATA;
        end
        DMA_DATA : begin 
            if((rx_bcnt_q == bdu_sz)|buf_eof) dma_ns = DMA_LAST;
        end
        DMA_LAST : begin 
            if(bdu_master.req&bdu_master.gnt) dma_ns =DMA_IDLE;
        end
    endcase 
end
//--------------------------------------------------------
//----
//----
//--------------------------------------------------------
enum logic[2:0] {MST_IDLE,WAIT_GNT,WAIT_VLD} bdu_cs,bdu_ns,mem_cs,mem_ns;
//--------------------------------------------------------
always_ff @(posedge clk_i,negedge rstn_i) begin 
    if(rstn_i == 0)begin 
        bdu_cs <= MST_IDLE;
    end
    else begin 
        bdu_cs <= bdu_ns;
    end
end
//--------------------------------------------------------
always_comb begin 
    bdu_ns = bdu_cs;
    bdu_master.addr  = dma_bdu_addr;
    bdu_master.req   = 'b0;
    bdu_master.we    = 'b0;
    bdu_master.wdata = 'b0;

    if (dma_cs == DMA_LAST) begin
        bdu_master.we    = 'b1;
        bdu_master.wdata = {bdu_sof,bdu_eof,bdu_err,13'b0,rx_bcnt_q[15:0]};
    end

    case(bdu_cs) 
        MST_IDLE: begin
            if(dma_bdu_op) begin 
                bdu_master.req = bdu_req;
                if(bdu_req) bdu_ns = (bdu_master.gnt)? WAIT_VLD : WAIT_GNT;
            end
        end
        WAIT_GNT: begin
            bdu_master.req = 1;
            if(bdu_master.gnt) bdu_ns = WAIT_VLD;
        end
        WAIT_VLD: begin 
            if(bdu_master.rvalid) begin 
                bdu_master.req = bdu_req;
                bdu_ns = (bdu_req&dma_bdu_op)?((bdu_master.gnt)? WAIT_VLD : WAIT_GNT) : MST_IDLE;
            end
        end
    endcase 
end
//--------------------------------------------------------
always_ff @ (posedge clk_i,negedge rstn_i) begin 
    if(rstn_i == 0)begin 
        dma_reset <= '0;
    end
    else if(dma_rx_en&dma_rx_valid&(dma_cs == DMA_IDLE)&(mem_cs == MST_IDLE))begin // auto reset in BD mode 
        dma_reset <= '1;
    end
    else if(dma_reset) begin 
        dma_reset <= '0;
    end
end
//--------------------------------------------------------
assign dma_bdu_op = ((dma_cs == DMA_CSR)||(dma_cs == DMA_LAST));
//--------------------------------------------------------
always_ff @(posedge clk_i,negedge rstn_i) begin 
    if(rstn_i == 0)begin 
        dma_bdu_addr <= 0;
    end
    else if (dma_reset) begin 
        dma_bdu_addr <= {dma_rx_rptr,3'b000};
    end
    else if (bdu_master.req&bdu_master.gnt) begin 
        dma_bdu_addr <= {dma_rx_rptr,3'b100};
    end
end
//--------------------------------------------------------
always_ff @(posedge clk_i,negedge rstn_i) begin 
    if(rstn_i == 0)begin 
        dma_rx_rptr <= '0;
    end
    else if (dma_ns == DMA_LAST) begin 
        dma_rx_rptr <= dma_rx_rptr + 1'b1;
    end
end 
//--------------------------------------------------------
always_ff @(posedge clk_i,negedge rstn_i) begin 
    if(rstn_i == 0)begin
        bdu_rd_cnt_q <= '0;
    end
    else if (dma_cs == DMA_IDLE) begin
        bdu_rd_cnt_q <= '0;
    end
    else if (bdu_master.rvalid) begin 
        bdu_rd_cnt_q <= bdu_rd_cnt;
    end
end
//--------------------------------------------------------
assign bdu_req    = ((dma_cs == DMA_CSR)&&(bdu_rd_cnt < BD_WCNT))||(dma_cs == DMA_LAST);
assign bdu_rd_cnt = (bdu_master.rvalid)?(bdu_rd_cnt_q + 1'b1):bdu_rd_cnt_q;
//--------------------------------------------------------
//----
//----
//--------------------------------------------------------
always_ff @(posedge clk_i,negedge rstn_i) begin 
    if(rstn_i == 0)begin 
        mem_cs <= MST_IDLE ;
    end
    else begin 
        mem_cs <= mem_ns;
    end
end
//--------------------------------------------------------
always_comb begin 
    mem_ns = mem_cs;
    mem_master.wdata= dma_mem_data;
    mem_master.addr = dma_mem_addr;
    mem_master.req  = 'b0;
    mem_master.we   = 'b1;
    case(mem_cs) 
        MST_IDLE: begin 
            if(dma_mem_valid) begin 
                mem_master.req = 1'b1;
                if(mem_master.gnt) begin 
                    mem_ns = WAIT_VLD;
                end
                else begin 
                    mem_ns = WAIT_GNT;
                end
            end
        end
        WAIT_GNT: begin 
            mem_master.req = 1'b1;
            if(mem_master.gnt) begin 
                mem_ns = WAIT_VLD;
            end
        end
        WAIT_VLD: begin 
            if(mem_master.rvalid) begin 
                mem_ns = MST_IDLE;
                if(dma_mem_valid) begin 
                    mem_master.req = 1'b1;
                    if(mem_master.gnt) begin 
                        mem_ns = WAIT_VLD;
                    end
                    else begin 
                        mem_ns = WAIT_GNT;
                    end
                end
            end
        end
    endcase 
end
//--------------------------------------------------------
always_ff @(posedge clk_i,negedge rstn_i) begin 
    if(rstn_i == 0)begin 
        dma_mem_addr <= 0;
    end
    else if ((bdu_rd_cnt_q == 0)&bdu_master.rvalid) begin 
        dma_mem_addr <= bdu_master.rdata;
    end    
    else if(mem_master.gnt)begin 
        dma_mem_addr <= {{dma_mem_addr[31:2] + 1'b1},2'b00};;
    end
end
//--------------------------------------------------------
assign dma_rx_ready = (dma_cs == DMA_DATA)&(~(mem_master.req&(~mem_master.gnt)));
//--------------------------------------------------- 
always_ff @(posedge clk_i,negedge rstn_i) begin 
    if(rstn_i == 0)begin 
        rx_bcnt_q <= 'b0;
    end
    else if(dma_reset)begin 
        rx_bcnt_q <= 'b0;
    end
    else if(dma_rx_ready&dma_rx_valid) begin
        rx_bcnt_q <= rx_bcnt_q + 1'b1;
    end
end
//--------------------------------------------------------
always_ff @(posedge clk_i,negedge rstn_i) begin 
    if(rstn_i == 0)begin 
        dma_rx_busy <= '0;
    end
    else if((bdu_cs == MST_IDLE)&&(mem_cs == MST_IDLE)&&(dma_cs == DMA_IDLE)) begin 
        dma_rx_busy <= '0;
    end
    else begin 
        dma_rx_busy <= '1;
    end
end
//--------------------------------------------------------
always_ff @(posedge clk_i,negedge rstn_i) begin 
    if(rstn_i == 0)begin 
        dma_rx_err <= '0;
    end
    else if(dma_reset) begin 
        dma_rx_err <= '0;
    end
    else begin 
        if(bdu_sz == 'b0) dma_rx_err[0] <= 1'b1; //data size should be more than 1 words 
    end
end
//--------------------------------------------------------
always_ff @(posedge clk_i,negedge rstn_i) begin 
    if(rstn_i == 0)begin 
        bdu_sz  <= 16'h256;
    end
    else if ((bdu_rd_cnt_q == 1)&bdu_master.rvalid) begin 
        bdu_sz <= bdu_master.rdata[15:0];
    end
end
//--------------------------------------------------------
always_ff @(posedge clk_i,negedge rstn_i) begin 
    if(rstn_i == 0)begin 
        bdu_err <= 1'b0;
        bdu_eof <= 1'b0;
        bdu_sof <= 1'b0;
    end
    else if (dma_reset) begin 
        bdu_err <= 1'b0;
        bdu_eof <= 1'b0;        
        bdu_sof <= 1'b0;
    end
    else begin
        if(rx_err) bdu_err <= 1'b1;
        if(rx_eof) bdu_eof <= 1'b1;
        if(rx_sof) bdu_sof <= 1'b1;
    end
end


endmodule
